import { WebGLRenderer, Scene, PerspectiveCamera, Vector3, Clock, SRGBColorSpace, ReinhardToneMapping, Layers, MeshBasicMaterial, Vector2, Raycaster, IcosahedronGeometry, Mesh, ShaderMaterial, AmbientLight, PointLight, LinearSRGBColorSpace, NoToneMapping, HemisphereLight, DirectionalLight, PCFShadowMap } from "three";
import { App } from "./App";
import Events from "./constants/Events";
import { TweenEx } from "../utils/Tween";
import { CSS2DRenderer } from "three/examples/jsm/renderers/CSS2DRenderer";

import Const from "./constants/Const";
import MaterialShaderManager from "../renderExtension/Material/MaterialShaderManager";
import ShaderConst from "../renderExtension/Material/ShaderConst";

class ThreeApplication {
    public renderer: WebGLRenderer;
    public css2dRender: CSS2DRenderer;
    public domElement: HTMLCanvasElement;
    public scene: Scene;
    public camera: PerspectiveCamera;
    orbit: any;
    protected clock: Clock;
    private options;

    constructor(options) {
        this.options = options;
        this.init();
    }
    private init() {
        this.clock = new Clock();
        //默认srgb颜色空间
        this.renderer = new WebGLRenderer({
            antialias: true,
            preserveDrawingBuffer: true,
            logarithmicDepthBuffer: false,//是否启用对数缓冲区
            alpha: true,
            stencil: true
        });
        this.renderer.sortObjects = true;
        this.renderer.localClippingEnabled = true;
        // this.renderer.outputColorSpace = "srgb-linear";
        this.renderer.shadowMap.enabled = true;

        this.domElement = this.renderer.domElement;
        this.domElement.id = Const.MapConst.mapCanvasID;
        this.domElement.style.position = 'absolute';
        this.domElement.style.userSelect = 'none'; //解决鼠标粘连的问题
        
        this.scene = new Scene();
        let div = document.getElementById(App.getInstance().div);

        //先添加css2drenderer
        this.css2dRender = new CSS2DRenderer();
        this.css2dRender.setSize(div.offsetWidth, div.offsetHeight);
        this.css2dRender.domElement.style.position = 'absolute';
        this.css2dRender.domElement.style.top = '0px';
        this.css2dRender.domElement.oncontextmenu = function () {
            return false;
        };
        div.appendChild(this.css2dRender.domElement);

        //新版threejs里必须手动将domelement添加至页面中
        div.appendChild(this.renderer.domElement);
        this.renderer.setPixelRatio(window.devicePixelRatio);
        this.renderer.setSize(div.offsetWidth, div.offsetHeight);

        //注册resize事件
        let scope = this;
        App.getInstance().on(Events.Resize, function () {
            scope.renderer.setSize(div.offsetWidth, div.offsetHeight);
            scope.css2dRender.setSize(div.offsetWidth, div.offsetHeight);
            if (App.getInstance().postProcess.effectComposer) {
                App.getInstance().postProcess.effectComposer.setSize(div.offsetWidth, div.offsetHeight);
            }
            if (App.getInstance().postProcess.bloomComposer) {
                App.getInstance().postProcess.bloomComposer.setSize(div.offsetWidth, div.offsetHeight);
            }
        })

        this.initResize(div);

        this.initCamera(div);

        this.initShaderConfig();

        this.update();
    }

    private initResize(div){//部分浏览器上在初始化div后，即使没有改变div的长宽，但是浏览器内部也有可能根据实际情况对尺寸做调整，因此必须监听
        const observer = new ResizeObserver((entries, observer) => {
            App.getInstance().fireEvent(Events.Resize);
            observer.disconnect();
        });
        observer.observe(div);
    }

    private initCamera(div){
        let aspect = div.offsetWidth / div.offsetHeight;
        let near = this.options.Camera.cameraNear;//20; //近裁剪面不能设置的太小，否则很容易出现深度冲突
        // let near = ((this.options.Map.maxZoom + 1) - nextZoom) * 10;
        // this.threeCamera.near = cameraNer;
        let far = 200000000; //远裁剪面
        this.camera = new PerspectiveCamera(this.options.Camera.cameraFov, aspect, near, far);

        let pos = new Vector3(100, 200, 300);
        this.camera.up.set(0, 0, 1);
        this.camera.position.set(pos.x, pos.y, pos.z);
    }

    private update() {
        let app = App.getInstance();
        let time = this.clock.getDelta();
        //如果着色通道被初始化就渲染着色通道
        if (app.postProcess && app.postProcess.enable && app.postProcess.effectComposer) {
            if (app.postProcess.effectComposer) {
                app.postProcess.effectComposer.render();
                let rainpass = app.postProcess.effectComposer.passes.find(item => item['PASSID'] === 'rainPass') 
                if(rainpass && rainpass.enabled) {
                     app.postProcess.setRainPass(true, {clockNumber:time});
                }

                let snowpass = app.postProcess.effectComposer.passes.find(item => item['PASSID'] === 'snowPass') 
                if(snowpass && snowpass.enabled) {
                     app.postProcess.setSnowPass(true, {clockNumber:time});
                }
            }
        }
        else {
            if (app.postProcess && app.postProcess.openSelectiveBloom) {
                app.postProcess.renderUnrealBloom();
            }
            else {
              
                this.renderer.render(this.scene, this.camera);
            }
        }

        this.css2dRender.render(this.scene, this.camera);

        if (app.stats)
            app.stats.update();
      
        TweenEx.update(time);
        app.fireEvent(Events.Render);
        requestAnimationFrame(this.update.bind(this));
    }

    private initShaderConfig(){
        let options = App.getInstance().options;
        MaterialShaderManager.addVertexCode(ShaderConst.vertexPointToWorld);
        MaterialShaderManager.addFragmentCode(ShaderConst.fragmentPointToWorld);

        let isOpenViewFrustum = options.Render.isOpenViewFrustum;
        if(isOpenViewFrustum == true){
            MaterialShaderManager.addVertexCode(ShaderConst.vertexWithinFrustum);
            MaterialShaderManager.addFragmentCode(ShaderConst.fragmentWithinFrustum);
        }
        
        let polygonPointNum = options.Render.polygonPointNumInShader;
        if(polygonPointNum > 0){
            MaterialShaderManager.addFragmentCode(ShaderConst.getFragmentWithinPolygon(polygonPointNum));
            MaterialShaderManager.addFragmentCode(ShaderConst.getFragmentCut(polygonPointNum));
            MaterialShaderManager.addFragmentCode(ShaderConst.getFragmentBloom(polygonPointNum));
        }
        
        let isOpenClapProject = options.Render.isOpenClapProject;
        if(isOpenClapProject){
            MaterialShaderManager.addFragmentCode(ShaderConst.fragmentClapProject);
        }
        
        let isOpenFlyline = options.Render.isOpenFlyline;
        if(isOpenFlyline){
            MaterialShaderManager.addVertexCode(ShaderConst.vertexFlyline);
            MaterialShaderManager.addFragmentCode(ShaderConst.fragmenetFlyline);
        }
        
        let isOpenTextureProject = options.Render.isOpenTextureProject;
        if(isOpenTextureProject){
            MaterialShaderManager.addVertexCode(ShaderConst.vertexTextureProject);
            MaterialShaderManager.addFragmentCode(ShaderConst.fragmentTextureProject);
        }
        
        let floorDivisionCount = options.ShapeConfig.floorDivisionCount;
        if(floorDivisionCount > 0){
            MaterialShaderManager.addFragmentCode(ShaderConst.getFragmentFloorDivision(floorDivisionCount));
        }

        let isOpenContourLine = options.ShapeConfig.isOpenContourLine;
        if(isOpenContourLine){
            MaterialShaderManager.addFragmentCode(ShaderConst.fragementcontourLine);
        }
        
        let isOpenCircleRange = options.Render.isOpenCircleRange;
        if(isOpenCircleRange == true){
            MaterialShaderManager.addFragmentCode(ShaderConst.fragmentCircleRange);
        }
    }
}

export { ThreeApplication };